
#include <BOpenGL>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BViewer>
#include <BCamera>
#include <BRender>
#include <BSpacer>
#include <BPainter>
#include <BFont>
#include <BLayout>
#include <BScene>
#include <BMessageBox>
#include <BLabel>
#include <BColorDialog>
#include <BFileDialog>
#include <BReadWrite>

#include "PixmapArea.h"
#include "PaintOperation.h"

PaintOperation::PaintOperation() : BEventFilter("paint")
{
	this->setName("Carver");

	_pressed = false;
	_shiftDown = false;
	_ctrlDown = false;
	_altDown = false;

	initUI();

	connect(this, Signal_AssetChanged, &PaintOperation::slotAssetChanged);
}
PaintOperation::~PaintOperation()
{

}

void PaintOperation::initUI()
{
	BLabel* imageLabel = new BLabel("image");
	_imageButton = new BButton("images");
	_imageButton->setForm(Form_IconOnly);
	_imageButton->setIconSize(100);
	_imageButton->setSizePolicy(Policy_Preferred | Policy_Expanding);

	BLabel* sizeLabel = new BLabel("size");
	_brushSizeSlider = new BSlider(0, 300);
	_brushSizeSlider->setGraph(Graph_Circle_Fill);
	_brushSizeSlider->setIconSize(16);
	_brushSizeSpinBox = new BSpinBox(0, 300);
	_brushSizeSpinBox->setSuffix("px");

	BLabel* brushColorLabel = new BLabel("color");
	_brushColorButton = new BButton(Graph_Square_Fill);
	_brushColorButton->setColor(255, 0, 0);

	_layout = new BHBoxLayout();
	_layout->addWidget(imageLabel, 0);
	_layout->addWidget(_imageButton, 0);
	_layout->addWidget(sizeLabel, 0);
	_layout->addWidget(_brushSizeSlider, 0);
	_layout->addWidget(_brushSizeSpinBox);
	_layout->addWidget(brushColorLabel, 0);
	_layout->addWidget(_brushColorButton, 0);

	connect(_imageButton, Signal_Clicked, &PaintOperation::slotImageButtonClicked);
	connect(_imageButton, Signal_RightClicked, &PaintOperation::slotImageButtonRightClicked);
	connect(_brushSizeSlider, Signal_ValueChanged, &PaintOperation::slotBrushSizeChanged);
	connect(_brushSizeSpinBox, Signal_ValueEdited, &PaintOperation::slotBrushSizeChanged);
	connect(_brushColorButton, Signal_Clicked, &PaintOperation::slotColorButtonClicked);

	_brushSizeSlider->setValue(10);
	_brushSizeSpinBox->setValue(10);

}
bool PaintOperation::intersectTest(const BQuad& quad, const BTriangle& trig)
{
	if (quad.normal().dot(trig.normal()) < 0)
		return false;
	if (quad.around(trig.a()) || quad.around(trig.b()) || quad.around(trig.c()))
	{
		return true;
	}
	if (trig.around(quad.a()))
		return true;
	if (trig.around(quad.b()))
		return true;
	if (trig.around(quad.c()))
		return true;
	if (trig.around(quad.d()))
		return true;
	return false;
}
void PaintOperation::catchPixels(const BQuad& quad)
{

}
void PaintOperation::paintPixels(const BQuad& quad)
{

}

bool IntersectCoord(const BCoord& a, const BCoord& b, const BCoord& c, float& lena, float& lenb)
{
	float len2 = c.length2();
	if (len2 <= 1e-8)
	{
		lena = 0;
		lenb = 0;
		return true;
	}
	float len = sqrt(len2);
	float A = c.dot(a);
	float cosa = A / len;
	float sina = sqrt(len2 - A * A) / len;
	float B = c.dot(b);
	float cosb = B / len;
	float sinb = sqrt(len2 - B * B) / len;
	float D = sina * cosb + cosa * sinb;
	if (bZero(D))
		return false;
	lena = sinb / D * len;
	lenb = sina / D * len;
	return true;
}
bool ContainCoord(const BCoord& a, const BCoord& b, const BCoord& c, const BCoord& d)
{
	BCoord da = a - d;
	BCoord db = b - d;
	BCoord dc = c - d;
	float dab = da.cross(db);
	float dbc = db.cross(dc);
	float dca = dc.cross(da);
	if (dab >= 0 && dbc >= 0 && dca >= 0)
		return true;
	if (dab <= 0 && dbc <= 0 && dca <= 0)
		return true;
	return false;
}
bool IntersectQuad(const BQuad& quad, const BQuad& other)
{
	BVector p;
	BVector ab = quad.b() - quad.a();
	BVector bc = quad.c() - quad.b();
	BVector cd = quad.d() - quad.c();
	BVector da = quad.a() - quad.d();
	BPlane pab(ab.cross(quad.normal()), quad.a());
	BPlane pbc(bc.cross(quad.normal()), quad.b());
	BPlane pcd(cd.cross(quad.normal()), quad.c());
	BPlane pda(da.cross(quad.normal()), quad.d());
	{
		BLine olab(other.a(), other.b());
		if (olab.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olab.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olab.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olab.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	{
		BLine olbc(other.b(), other.c());
		if (olbc.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olbc.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olbc.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olbc.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	{
		BLine olcd(other.c(), other.d());
		if (olcd.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olcd.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olcd.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olcd.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	{
		BLine olda(other.d(), other.a());
		if (olda.intersect(pab, p) && ((p - quad.a()).dot(ab) > 0 && ((p - quad.b()).dot(ab) < 0)))
			return true;
		if (olda.intersect(pbc, p) && ((p - quad.b()).dot(bc) > 0 && ((p - quad.c()).dot(bc) < 0)))
			return true;
		if (olda.intersect(pcd, p) && ((p - quad.c()).dot(cd) > 0 && ((p - quad.d()).dot(cd) < 0)))
			return true;
		if (olda.intersect(pda, p) && ((p - quad.d()).dot(da) > 0 && ((p - quad.a()).dot(da) < 0)))
			return true;
	}
	return false;
}

void PaintOperation::slotAssetChanged(BObject* object, const BValue& value)
{

}

void PaintOperation::slotImageButtonClicked(BObject* object, const BValue& value)
{
	const BString& fileName = BFileDialog::OpenFile("Choose Brush Image", "./data/image");
	if (fileName.size())
	{
		BImage* image = ReadFile<BImage>(fileName);
		_imageButton->setImage(image);
	}
}
void PaintOperation::slotImageButtonRightClicked(BObject* object, const BValue& value)
{
	_imageButton->setImage(0);
}
void PaintOperation::slotBrushSizeChanged(BObject* object, const BValue& value)
{
	if (object == _brushSizeSlider)
	{
		int size = value;
		_brushSizeSpinBox->setValue(size);
	}
	if (object == _brushSizeSpinBox)
	{
		int size = value;
		_brushSizeSlider->setValue(size);
	}
}
void PaintOperation::slotColorButtonClicked(BObject* object, const BValue& value)
{
	BColor color = BColorDialog::ChooseColor("Choose Button Color", _brushColorButton->color());
	_brushColorButton->setColor(color);
}

void PaintOperation::attached(BWidget* widget)
{
	widget->setLayout(_layout, Part_None);
}
void PaintOperation::detached(BWidget* widget)
{
	widget->removeLayout(_layout);
}

void PaintOperation::freshEvent(const BEvent& event)
{
	PixmapArea* pixmapArea = dynamic_cast<PixmapArea*>(this->host());
	BRect crect = pixmapArea->centerRect();
	BSize hsize = _layout->sizeHint();
	BRect layoutRect = crect.align(Align_RightBottom, hsize);
	_layout->setRect(layoutRect);
	_layout->adjust();
}

void PaintOperation::paint(BPainter& painter)
{
	painter.setColor(_brushColorButton->color());
	if (BImage* image = _imageButton->image())
	{
		painter.drawImage(_brushRect, image);
	}
	int brushSize = _brushSizeSpinBox->value();
	painter.drawCircle(_brushRect.center(), brushSize);
	}
void PaintOperation::updateEvent(const BEvent& event)
{
	if (_pressed)
	{
		PixmapArea* pixmapArea = dynamic_cast<PixmapArea*>(this->host());
		BColor brushColor = _brushColorButton->color();
		BRect brushRect = pixmapArea->mapToPixmap(_brushRect);
		BReal radius = brushRect.width() / (BReal)2;

		BPixmap* pixmap = this->asset();
		for (int i = 0; i < pixmap->widgetCount(); i++)
		{
			ImageLayer* layer = (ImageLayer*)pixmap->widget(i);
			if (!layer->visible())
				continue;
			BImage* image = layer->image();
			BRect rect = brushRect - layer->pos();
			BPoint center = rect.center();
			rect &= BRect(0, 0, image->width(), image->height());
			if (rect.empty())
				continue;
			for (int y = rect.top(); y < rect.bottom(); y++)
			{
				BByte* rowBytes = image->bytes(y);
				for (int x = rect.left(); x < rect.right(); x++)
				{
					BReal length = (BReal)(center - BPoint(x, y)).length();
					BReal ratio = (radius - length) / radius;
					if (ratio < 0)
						continue;
					BColor color = brushColor;
					if (image->format() == Format_RGB)
					{
						BByte* pixel = rowBytes + x * 3;
						pixel[0] = BByte(pixel[0] * (1.0 - ratio) + color.r() * ratio);
						pixel[1] = BByte(pixel[1] * (1.0 - ratio) + color.g() * ratio);
						pixel[2] = BByte(pixel[2] * (1.0 - ratio) + color.b() * ratio);
					}
					else if (image->format() == Format_RGBA)
					{
						BByte* pixel = rowBytes + x * 4;
						pixel[0] = BByte(pixel[0] * (1.0 - ratio) + color.r() * ratio);
						pixel[1] = BByte(pixel[1] * (1.0 - ratio) + color.g() * ratio);
						pixel[2] = BByte(pixel[2] * (1.0 - ratio) + color.b() * ratio);
						pixel[3] = BByte(pixel[3] * (1.0 - ratio) + color.a() * ratio);
					}
				}
			}
			image->dirty();
		}
	}
}

void PaintOperation::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_LShift || keyEvent.key() == Key_RShift)
	{
		_shiftDown = true;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_LCtrl || keyEvent.key() == Key_RCtrl)
	{
		_ctrlDown = true;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_Space)
	{
	}
}
void PaintOperation::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_LShift || keyEvent.key() == Key_RShift)
	{
		_shiftDown = false;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_LCtrl || keyEvent.key() == Key_RCtrl)
	{
		_ctrlDown = false;
		keyEvent.accept();
	}
}

void PaintOperation::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		_pressed = true;
		mouseEvent.accept();
	}
}
void PaintOperation::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (_pressed)
	{
		_pressed = false;
		mouseEvent.accept();
	}
}
void PaintOperation::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	{
		BPoint center = mouseEvent.pos();
		_brushRect.reset(center);
		_brushRect.expand(BSize(_brushSizeSpinBox->value() * 2));
		this->fresh();
	}
	mouseEvent.accept();
}

void PaintOperation::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	mouseEvent.accept();
}
